package com.ruoyi.project.wf.engine.handlers.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.expression.ExpressionUtil;
import com.ruoyi.project.wf.engine.core.Execution;
import com.ruoyi.project.wf.engine.handlers.IHandler;
import com.ruoyi.project.wf.engine.model.TaskModel;
import com.ruoyi.project.wf.enums.CountersignTypeEnum;
import com.ruoyi.project.wf.enums.FlowConst;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 会签任务处理，用于判断会签任务是否可通过
 * @author mldong
 * @date 2023/11/24
 */
public class CountersignHandler implements IHandler {
    private TaskModel taskModel;
    public CountersignHandler(TaskModel taskModel) {
        this.taskModel = taskModel;
    }
    @Override
    public void handle(Execution execution) {
        // 这里怎么从ext获取了?能取到吗？
        String countersignType = taskModel.getExt().get("countersignType","PARALLEL");
        String countersignCompletionCondition = taskModel.getExt().get("countersignCompletionCondition","");
        String prefix = FlowConst.COUNTERSIGN_VARIABLE_PREFIX +taskModel.getName()+"_";
        // 会签办理人列表
        List<String> operatorList = Convert.toList(String.class,execution.getArgs().get(prefix+FlowConst.COUNTERSIGN_OPERATOR_LIST));
        // 循环计数器，办理人在列表中的索引,看来只在串行会签时有用.
        int loopCounter = operatorList.indexOf(execution.getOperator());
        // 追加计数器
        execution.getArgs().put(prefix+FlowConst.LOOP_COUNTER,loopCounter);
        // 追加已完成数量
        // 这里很奇怪,不应该判断是同意还是拒绝吗？
        Dict addVariable = Dict.create();
        // 当前这次task是通过还是失败
        boolean curPass = !execution.getArgs().containsKey(FlowConst.COUNTERSIGN_DISAGREE_FLAG);
        if(!curPass) {
            // 如果是会签串行,这里暂时先按照流程结束处理
            if(countersignType.equals("SEQUENTIAL")) {
                // 直接拒绝
                execution.getEngine().processInstanceService().rejectProcessInstance(execution.getProcessInstanceId());
                return ;
            }
            // 并行情况,如果是拒绝,把拒绝实例数更新到到args
            execution.getArgs().put(prefix + FlowConst.NR_OF_DISAGREE_INSTANCES,
                    execution.getArgs().get(prefix + FlowConst.NR_OF_DISAGREE_INSTANCES,0)+1);
            addVariable.put(prefix + FlowConst.NR_OF_DISAGREE_INSTANCES, execution.getArgs().get(prefix+FlowConst.NR_OF_DISAGREE_INSTANCES));
        } else {
            // 如果是同意,更新完成实例数
            execution.getArgs().put(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES,
                    execution.getArgs().get(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES,0)+1);
            addVariable.put(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES, execution.getArgs().get(prefix+FlowConst.NR_OF_COMPLETED_INSTANCES));
        }
        // 更新到流程实例变量集合
        execution.getEngine().processInstanceService().addVariable(execution.getProcessInstanceId(), addVariable);

        /**
         * ● 全部通过：为空
         * ● 按数量通过：#nrOfCompletedInstances==n，这里表示n人完成任务，会签结束。
         * ● 按比例通过：#nrOfCompletedInstances/nrOfInstances==n，这里表示已完成会签数与总实例数达到一定比例时，会签结束
         * ● 一票通过：#nrOfCompletedInstances==1，这里表示1人完成任务，会签结束。
         * ● 一票否决：ONE_VOTE_VETO
         */
        boolean isPassed = true; // 表示是否会签通过
        // isMerged表示会签是否可合并,也就是会签是否可以认为结束了.
        boolean isMerged = false; // 表示会签是否结束,只有isMerged结束时,表示会签不再需要继续,而isPassed则表示是会签成功还是会签失败.
        if("ONE_VOTE_VETO".equalsIgnoreCase(countersignCompletionCondition)) {
            // 一票否决
            if(execution.getArgs().containsKey(FlowConst.COUNTERSIGN_DISAGREE_FLAG)) {
                // 存在拒绝标识，则直接通过
                isMerged = true;
                isPassed = false;
            }
        } else if(!StrUtil.isBlank(countersignCompletionCondition)) {
            // 根据条件判断是否通过
            // 这里有一个小坑: 对于会签串行的情况来说,总实例数不准,因为他一次产生一个任务,所以这时候比较好的方法是判断成功的个数,而不是比例.
            Dict dict = Dict.create();
            execution.getArgs().forEach((k,v)->{
                dict.set(k.replace(prefix,""),v);
            });
            // 我想,绝大部分情况下,会在这个地方提前满足条件,也就是这里isMerged和isPassed都是true.
            isMerged = Convert.toBool(ExpressionUtil.eval(countersignCompletionCondition, dict));
            isPassed = isMerged;
        }
        // 通过上面代码可以看出,isMerged的意思,就是是否会签已满足的意思,会签满足的意思要么是一票通过,要么是按表达式.
        if(!isMerged && CountersignTypeEnum.SEQUENTIAL.toString().equalsIgnoreCase(countersignType)) {
            // 串行未通过，则判断是否为最后一个
            if (loopCounter == operatorList.size() - 1) {
                isMerged = true;
                isPassed = true;
            } else {
                // 非最后一个，则继续创建会签任务
                execution.getEngine().processTaskService().createCountersignTask(taskModel, execution);
                // 串行流程,新生成了任务,直接返回得了
                return ;
            }
        }
        int activeInstances = 0;
        // 这里我先默许,会签串行就是全员通过,也就是这种的不需要配置通过条件.
        // 那么对于实例已经全都走完了的情况.对于并行来说:我觉得一定要有配置条件,所以这时候还没通过的那就是没通过了,就是流程结束.
        // 对于串行来说,本来也不需要配置条件,这种也就不用考虑其他情况了,直接在任何一个不通过时结束流程.
        // 更好的办法是,针对串行和并行仔分成两个handler来进行处理.
        if(!isMerged) {
            activeInstances = execution.getEngine().processTaskService().getDoingTaskList(execution.getProcessInstanceId(), new String[]{taskModel.getName()}).size();
            // 这个地方已经缩小为并行的情况了
            if(activeInstances==0) {
                // 没有活动实例了,条件也还没有满足,那就相当于会签没通过,直接拒绝
                execution.getEngine().processInstanceService().rejectProcessInstance(execution.getProcessInstanceId());
                return ;
            }
        }
//        if(!isMerged && CountersignTypeEnum.PARALLEL.toString().equalsIgnoreCase(countersignType)) {
//            // 是否所有会签任务已完成,如果还有运行中的任务,表示会签还没有结束(本次完成的任务已经在早些时候finishTask了,现在处于触发taskModel.execute中)
//            // 如果是并行,就不考虑通过还是拒绝了吗？
//            // 这根本没考虑会签拒绝的情况啊
//            isMerged = execution.getEngine().processTaskService().getDoingTaskList(execution.getProcessInstanceId(), new String[]{taskModel.getName()}).size()==0;
//            if(!isMerged) {
//                // 未通过，更新已完成实例数量
//                Dict addVariable = Dict.create();
//                addVariable.put(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES, execution.getArgs().get(prefix+FlowConst.NR_OF_COMPLETED_INSTANCES));
//                execution.getEngine().processInstanceService().addVariable(execution.getProcessInstanceId(), addVariable);
//            }
//        }
        // 如果已经没有激活的任务实例了,并且并没有最终通过. 这里分两种情况:1.串行的情况,都已经完成了. 2.
        if(isMerged) { // 合并表示条件已满足,但通过结果可能是也可能否
            if(!isPassed) {
                execution.getEngine().processInstanceService().rejectProcessInstance(execution.getProcessInstanceId());
                return ;
            }
            // 获取所有会签参数键值
            List<String> keys = execution.getArgs().keySet().stream().filter(k->k.startsWith(prefix)).collect(Collectors.toList());
            keys.add(FlowConst.COUNTERSIGN_DISAGREE_FLAG);
            // 如果可以合并，则把流程实例中的会签参数清空
            execution.getEngine().processInstanceService().removeVariable(
                    execution.getProcessInstanceId(),
                    keys.toArray(new String[]{}));
            // 如果为并行会签，需将其他会签任务设置为废弃,如果有的话.
            if(CountersignTypeEnum.PARALLEL.toString().equalsIgnoreCase(countersignType)) {
                execution.getEngine().processTaskService().getDoingTaskList(execution.getProcessInstanceId(), new String[]{taskModel.getName()}).stream().forEach(t->{
                    execution.getEngine().processTaskService().abandonProcessTask(t.getId(), FlowConst.AUTO_ID, execution.getArgs());
                });
            }
        }
        execution.setMerged(isMerged);
    }
}
